﻿using Single.Core.Service;
using Single.Sugar.Service;
using Single.Sys.Entity;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Text;
using Single.Encrypt;
using SqlSugar;
using Single.Json;
using Single.Cache;
using Single.Lock;
using System.Security.Cryptography;

namespace Single.Sys.Service
{
    public class SysUserService : BaseService<SysUser>
    {
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public ServiceResult SignIn(string username, string password)
        {
            var lockRep = MoreRep<SysSignInLock>();

            var lockSignIn = lockRep.GetSingle(m => m.Username == username);

            // 如果账号已经锁定则不进行登录校验
            if (lockSignIn != null && lockSignIn.UnlockTime > DateTime.Now)
            {
                var minutes = (int)(lockSignIn.UnlockTime - DateTime.Now).TotalMinutes + 1;
                return FailureResult($"由于多次密码错误账号已经锁定，目前剩余 {minutes} 分钟");
            }

            password = EncryptHelper.Md5(password);

            var entity = rep.GetSingle(m => m.Username == username && m.Password == password);

            if (entity != null)
            {
                if (entity.Status == "ok")
                {
                    return SuccessResult("登录成功", data: entity);
                }
                else { return FailureResult("用户已禁用"); }
            }

            // 如果登录失败则对输入的账号执行试错锁定策略
            var cfgs = MoreRep<SysConfig>().AsQueryable().Where(m => m.Code.StartsWith("pwd_strategy_lock")).ToDictionary(m => m.Code, m => m);

            if (cfgs.Count > 0 && cfgs.ContainsKey("pwd_strategy_lock_switch"))
            {
                var lockSwitch = cfgs["pwd_strategy_lock_switch"] as SysConfig;

                if (lockSwitch != null && !string.IsNullOrEmpty(lockSwitch.Content) && bool.TryParse(lockSwitch.Content, out bool lockSwitchValue) && lockSwitchValue)
                {
                    var lockNumber = cfgs["pwd_strategy_lock_number"] as SysConfig;

                    if (lockNumber != null && !string.IsNullOrEmpty(lockNumber.Content) && int.TryParse(lockNumber.Content, out int lockNumberValue))
                    {
                        var cacheKey = $"pwd_strategy_lock_attempts:{username}";

                        // 试错次数通过缓存记录
                        var attempts = CacheHelper.GetOrCreate(cacheKey, 20, () => 0) + 1;

                        if (attempts >= lockNumberValue)
                        {
                            var lockMinute = cfgs["pwd_strategy_lock_minute"] as SysConfig;

                            if (lockMinute != null && !string.IsNullOrEmpty(lockMinute.Content) && int.TryParse(lockMinute.Content, out int lockMinuteValue))
                            {
                                if (lockSignIn == null)
                                {
                                    lockSignIn = new SysSignInLock { Username = username, LockNumber = 0 };
                                }

                                lockSignIn.UnlockTime = DateTime.Now.AddMinutes(lockMinuteValue);

                                // 执行账号试错锁定
                                lockRep.InsertOrUpdate(lockSignIn);

                                // 移除缓存
                                CacheHelper.Remove(cacheKey);
                            }
                        }
                        else { CacheHelper.Set(cacheKey, attempts); }
                    }
                }
            }

            return FailureResult("帐号或密码错误");
        }

        /// <summary>
        /// 后台管理列表
        /// </summary>
        /// <param name="pageNum"></param>
        /// <param name="pageSize"></param>
        /// <param name="orgId"></param>
        /// <param name="username"></param>
        /// <param name="name"></param>
        /// <param name="status"></param>
        /// <param name="total"></param>
        /// <returns></returns>
        public List<SysUser> GetPageList(int pageNum, int pageSize, long orgId, string username, string name, string status, out int total)
        {
            var query = rep.AsQueryable().Includes(m => m.SysOrg).OrderBy(m => m.CreateTime, OrderByType.Desc);

            query = query.WhereIF(orgId != 0, m => m.SysOrg.Id == orgId || m.SysOrg.TreePath.Contains($"|{orgId}|"));

            query = query.WhereIF(!string.IsNullOrEmpty(username), m => m.Username.Contains(username))
                .WhereIF(!string.IsNullOrEmpty(name), m => m.Name.Contains(name))
                .WhereIF(!string.IsNullOrEmpty(status), m => m.Status.Contains(status));

            return query.ToPageList(pageNum, pageSize, out total);
        }

        /// <summary>
        /// 重写保存
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public override ServiceResult InsertOrUpdate(SysUser entity)
        {
            if (entity.Id > 0)
            {
                var line = rep.AsUpdateable(entity)
                    .IgnoreColumns(m => new { m.Username, m.Password }).ExecuteCommand();

                return UnknownResult(line > 0);
            }
            else
            {
                entity.Password = EncryptHelper.Md5(entity.Password);
                entity.PasswordTime = DateTime.Now;
                entity.PasswordHistorical = JsonHelper.Serialize(new string[] { entity.Password });

                return Insert(entity);
            }
        }

        /// <summary>
        /// 自动注册
        /// </summary>
        /// <param name="workcode"></param>
        /// <param name="oaid"></param>
        /// <param name="lastname"></param>
        /// <returns></returns>
        public SysUser GetOrInsert(string workcode, string lastname)
        {
            return MutexHelper.UseWaitLock("local", "auto_register", workcode, 1, () =>
            {
                var entity = rep.AsQueryable().Single(m => m.Username == workcode);

                if (entity == null)
                {
                    entity = new SysUser();

                    entity.InitId();
                    entity.Username = workcode;
                    entity.Password = EncryptHelper.Md5(entity.Id.ToString());
                    entity.PasswordTime = DateTime.Now;
                    entity.PasswordHistorical = JsonHelper.Serialize(new string[] { entity.Password });
                    entity.Name = lastname;
                    entity.Status = "ok";

                    Insert(entity);
                }

                return entity;
            });
        }

        /// <summary>
        /// 重置密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public ServiceResult ResetPassword(long id, string password)
        {
            var user = Get(id);

            if (user != null)
            {
                password = EncryptHelper.Md5(password);

                var historical = new List<string>();

                if (!string.IsNullOrEmpty(user.PasswordHistorical))
                {
                    historical = JsonHelper.Deserialize<List<string>>(user.PasswordHistorical);
                }

                historical.Add(password);

                if (historical.Count > 10) { historical.RemoveRange(0, historical.Count - 10); }

                user.Password = password;
                user.PasswordTime = DateTime.Now;
                user.PasswordHistorical = JsonHelper.Serialize(historical);

                var bit = rep.UpdateFields(user, "Password", "PasswordTime", "PasswordHistorical");

                return UnknownResult(bit, "密码重置成功");
            }

            return FailureResult("没有找到用户信息");
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="oldPwd"></param>
        /// <param name="newPwd"></param>
        /// <returns></returns>
        public ServiceResult ChangePassword(long id, string oldPwd, string newPwd)
        {
            var entity = Get(id);

            if (entity != null)
            {
                oldPwd = EncryptHelper.Md5(oldPwd);

                if (entity.Password == oldPwd)
                {
                    newPwd = EncryptHelper.Md5(newPwd);

                    var historical = new List<string>();

                    if (!string.IsNullOrEmpty(entity.PasswordHistorical))
                    {
                        historical = JsonHelper.Deserialize<List<string>>(entity.PasswordHistorical);
                    }

                    var cfgs = MoreRep<SysConfig>().AsQueryable().Where(m => m.Code.StartsWith("pwd_strategy_historical")).ToDictionary(m => m.Code, m => m);

                    if (cfgs.Count > 0 && cfgs.ContainsKey("pwd_strategy_historical_switch"))
                    {
                        var historicalSwitch = cfgs["pwd_strategy_historical_switch"] as SysConfig;

                        if (historicalSwitch != null && !string.IsNullOrEmpty(historicalSwitch.Content) && bool.TryParse(historicalSwitch.Content, out bool historicalSwitchValue) && historicalSwitchValue)
                        {
                            var historicalNumber = cfgs["pwd_strategy_historical_number"] as SysConfig;

                            if (historicalNumber != null && !string.IsNullOrEmpty(historicalNumber.Content) && int.TryParse(historicalNumber.Content, out int historicalNumberValue))
                            {
                                var topHistoricalKeys = historical.Count > historicalNumberValue ? historical.Skip(Math.Max(0, historical.Count - historicalNumberValue)).ToList() : historical;

                                if (topHistoricalKeys.Contains(newPwd))
                                {
                                    return FailureResult($"新密码与最近 {historicalNumberValue} 次修改记录重复，请使用其他密码");
                                }
                            }
                        }
                    }

                    historical.Add(newPwd);

                    if (historical.Count > 10) { historical.RemoveRange(0, historical.Count - 10); }

                    entity.Password = newPwd;
                    entity.PasswordTime = DateTime.Now;
                    entity.PasswordHistorical = JsonHelper.Serialize(historical);

                    var bit = rep.UpdateFields(entity, "Password", "PasswordTime", "PasswordHistorical");

                    return UnknownResult(bit, "密码修改成功");
                }

                return FailureResult("旧密码不正确");
            }

            return FailureResult("没有找到用户信息");
        }

        public ServiceResult PasswordIsExpire(DateTime pwdTime)
        {
            var cfgs = MoreRep<SysConfig>().AsQueryable().Where(m => m.Code.StartsWith("pwd_strategy_expire")).ToDictionary(m => m.Code, m => m);

            if (cfgs.Count > 0 && cfgs.ContainsKey("pwd_strategy_expire_switch"))
            {
                var expireSwitch = cfgs["pwd_strategy_expire_switch"] as SysConfig;

                if (expireSwitch != null && !string.IsNullOrEmpty(expireSwitch.Content) && bool.TryParse(expireSwitch.Content, out bool expireSwitchValue) && expireSwitchValue)
                {
                    var expireDay = cfgs["pwd_strategy_expire_day"] as SysConfig;

                    if (expireDay != null && !string.IsNullOrEmpty(expireDay.Content) && int.TryParse(expireDay.Content, out int expireDayValue))
                    {
                        return UnknownResult(pwdTime.AddDays(expireDayValue) < DateTime.Now, "密码已过期", "密码有效");
                    }
                }
            }

            return FailureResult();
        }
    }
}